package cn.iocoder.yudao.module.ent.service.search;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.ent.controller.admin.company.vo.QueryConditionFormVO;
import cn.iocoder.yudao.module.ent.controller.admin.company.vo.QueryRespVO;
import cn.iocoder.yudao.module.ent.controller.admin.company.vo.QueryVO;
import cn.iocoder.yudao.module.ent.document.EntCompany;
import cn.iocoder.yudao.module.ent.document.node.NodeContactList;
import cn.iocoder.yudao.module.ent.enums.EntAreaConvert;
import cn.iocoder.yudao.module.ent.enums.EntConstants;
import cn.iocoder.yudao.module.ent.enums.EntSearchReportEnum;
import cn.iocoder.yudao.module.ent.enums.search.EntFormTypeEnum;
import cn.iocoder.yudao.module.ent.enums.search.EntSearchConditionEnum;
import cn.iocoder.yudao.module.ent.enums.search.EntSearchFieldNotKeywordEnum;
import cn.iocoder.yudao.module.ent.enums.search.EntSearchOperationEnum;
import cn.iocoder.yudao.module.ent.mq.producer.report.EntReportProducer;
import cn.iocoder.yudao.module.ent.utils.EsQueryValueConvertUtils;
import cn.iocoder.yudao.module.ent.utils.NodeMerger;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.module.ent.enums.ErrorCodeConstants.QUERY_ERROR_MISS_PARAMS;


import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.common.lucene.search.function.CombineFunction;

@Service
@Validated
@Slf4j
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private EntReportProducer entReportProducer;

    /**
     * 默认查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    @Override
    public QueryRespVO<EntCompany> queryList(QueryVO queryVO) {
        // 预处理查询条件
        queryVO = MergerCondition(queryVO);
        makeDefaultValue(queryVO); // 默认添加 entType 字段

        // 创建查询构建器
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();

        // 构建 bool 查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.filter(QueryBuilders.existsQuery("entType"));

        if (CollectionUtil.isEmpty(queryVO.getOperations())) {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
            boolQueryBuilder = builderBoolQueryOperation(queryVO.getOperations());
        }

        // 构建 function_score 查询，提升 entType 为 "公司" 的文档权重
        MatchQueryBuilder entTypeIsCompany = QueryBuilders.matchQuery("entType", "公司");
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        entTypeIsCompany,
                        ScoreFunctionBuilders.weightFactorFunction(10.0f)
                )
        };

        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder, functions)
                .scoreMode(FunctionScoreQuery.ScoreMode.MULTIPLY)
                .boostMode(CombineFunction.MULTIPLY);

        // 设置查询语句
        query.withQuery(functionScoreQueryBuilder);

        // 打印调试信息
        log.debug("ES query : {}", functionScoreQueryBuilder.toString());

        // 分页设置
        fixMoreThanPageSize(queryVO);
        query.withPageable(PageRequest.of(queryVO.getPageNo() - 1, queryVO.getPageSize()));

        // 排序：先按 entType.keyword，再按 _score
        List<Sort.Order> orders = ListUtil.toList(
//                Sort.Order.desc("entType.keyword"),
                Sort.Order.desc("_score")
        );
        if (CollUtil.isNotEmpty(queryVO.getSortList())) {
            queryVO.getSortList().forEach(data -> {
                orders.add(Sort.Order.desc(data.getField()));
            });
        }
        query.withSort(Sort.by(orders));
        log.info("ES sort : {}", Sort.by(orders));

        // 查询执行
        long start = System.currentTimeMillis();
        long totalHits = 0;
        List<EntCompany> resultList = new ArrayList<>();
        try {
            SearchHits<EntCompany> search = elasticsearchRestTemplate.search(query.build(), EntCompany.class);
            totalHits = search.getTotalHits();
            resultList = search.getSearchHits()
                    .stream()
                    .map(SearchHit::getContent)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("ES query error: {}", e.getMessage(), e);
        }
        long end = System.currentTimeMillis();

        // 返回封装结果
        return new QueryRespVO<EntCompany>()
                .setPageNo(queryVO.getPageNo())
                .setPageSize(queryVO.getPageSize())
                .setTotal(totalHits)
                .setTiming(end - start)
                .setResults(resultList);
    }

    /**
     * 1、查询表达式校验
     * 2、查询表达式合并优化
     *
     * @param queryVO
     */
    public QueryVO MergerCondition(QueryVO queryVO) {
        // 获取高级查询模板
        List<QueryConditionFormVO.QueryFieldsVO> queryConditionTemplate = collectAllQueryFields(QueryConditionFormVO.build());
        // 递归校验模板，校验不通过的强制按模板赋值
        if (queryVO != null && queryVO.getOperations() != null) {
            checkFromOperations(queryVO.getOperations(), queryConditionTemplate);
        }
        String queryJson = JSONUtil.toJsonStr(queryVO);
        JSONObject queryData = new JSONObject(queryJson);
        Object result = NodeMerger.mergeSimilarNodes(queryData);
        NodeMerger.deduplicateInValues(result);
        return JSONUtil.toBean(result.toString(), QueryVO.class);
    }

    /**
     * 校验查询表达式
     *
     * @param operations
     * @param queryConditionTemplate
     */
    private void checkFromOperations(List<QueryVO.QueryOperation> operations, List<QueryConditionFormVO.QueryFieldsVO> queryConditionTemplate) {
        if (operations == null || queryConditionTemplate == null) {
            return;
        }
        // 构建模板字段映射，提高查找效率
        Map<String, QueryConditionFormVO.QueryFieldsVO> templateMap = queryConditionTemplate.stream().filter(vo -> vo.getField() != null)
                .collect(Collectors.toMap(QueryConditionFormVO.QueryFieldsVO::getField, Function.identity(), (existing, replacement) -> existing));  // 合并策略：保留首次出现的值

        for (QueryVO.QueryOperation op : operations) {
            // 处理当前 Operation 的 queryFields
            if (op.getQueryFields() != null) {
                filterInvalidQueryFields(op, templateMap);
            }

            // 递归处理子 Operations
            if (op.getOperations() != null) {
                checkFromOperations(op.getOperations(), queryConditionTemplate);
            }
        }
    }

    /**
     * 过滤不符合模板条件的 QueryField
     */
    private void filterInvalidQueryFields(
            QueryVO.QueryOperation op,
            Map<String, QueryConditionFormVO.QueryFieldsVO> templateMap
    ) {
        Iterator<QueryVO.QueryFieldVO> iterator = op.getQueryFields().iterator();
        while (iterator.hasNext()) {
            QueryVO.QueryFieldVO queryField = iterator.next();
            String field = queryField.getField();

            // 检查模板中是否存在该字段
            QueryConditionFormVO.QueryFieldsVO templateField = templateMap.get(field);
            boolean isFieldExist = (templateField == null);

            // 如果模板中没有此字段则移除
            if (isFieldExist) {
                iterator.remove();
            } else {
                //强制替换为模板中的查询类别及表单类别
                if (queryField.getQueryType() == null) {
                    queryField.setQueryType(templateField.getQueryType());
                }
                queryField.setFormType(templateField.getFormType());
            }
        }
    }

    //将查询表达式模板扁平化，利于之后校验
    public List<QueryConditionFormVO.QueryFieldsVO> collectAllQueryFields(List<QueryConditionFormVO.QueryFieldsVO> roots) {
        List<QueryConditionFormVO.QueryFieldsVO> result = new ArrayList<>();
        if (roots == null) {
            return result; // 空列表更安全，避免 NPE
        }
        for (QueryConditionFormVO.QueryFieldsVO root : roots) {
            collectRecursively(root, result);
        }
        return result;
    }

    private void collectRecursively(QueryConditionFormVO.QueryFieldsVO node, List<QueryConditionFormVO.QueryFieldsVO> result) {
        if (node == null) {
            return;
        }
        // 添加当前节点到结果列表
        result.add(node);
        // 递归处理子节点
        List<QueryConditionFormVO.QueryFieldsVO> children = node.getChildren() != null ? node.getChildren() : Collections.emptyList();
        if (children != null) {
            for (QueryConditionFormVO.QueryFieldsVO child : children) {
                collectRecursively(child, result);
            }
        }
    }

    //获取查询字段提示字典
    public Set<String> queryDicByField(String field, String value, String attribute) {
        NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder();
        if (value.trim().equals("")) {
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
                    .must(QueryBuilders.existsQuery(field));
            searchQuery.withQuery(boolQuery);
        } else {
            searchQuery.withQuery(QueryBuilders.matchQuery(field, value)); // 条件查询
        }
        searchQuery.withSourceFilter(new FetchSourceFilter(new String[]{attribute}, null)); // 指定返回的字段
        SearchHits<EntCompany> search = elasticsearchRestTemplate.search(searchQuery.build(), EntCompany.class);
        List<EntCompany> companyList = search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        Set<String> resultList = new HashSet<>();
        companyList.forEach(data -> {
            // 反射动态调用属性的get方法
            Class<? extends EntCompany> clazz = data.getClass();
            String methodName = "get" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1);
            try {
                Method method = clazz.getMethod(methodName);
                String result = (String) method.invoke(data);
                String[] courtArray = result.split(",");
                for (String court : courtArray) {
                    if (court.contains(value)) {
                        resultList.add(court);
                    }
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return resultList;
    }

    private void makeDefaultValue(QueryVO queryVO) {
        /**
         * 正常的企业查询. 首先满足企业在前.个体在后的原则.
         * 在没有任何企业类型指定的情况下. 就让他 entType 不含有个体工商户
         */

        // 1. 默认给entType赋值一个must_not: 个体工商户
        AtomicBoolean hasEntType = new AtomicBoolean(false);
        queryVO.getOperations().forEach(queryOperation -> {
            queryOperation.getQueryFields().forEach(queryFieldVO -> {
                if (queryFieldVO.getField().equals("entType")) {
                    hasEntType.set(true);
                }
            });
        });
        //去除直接过滤,根据entType排序
//        if (!hasEntType.get()) {
//            List<QueryVO.QueryOperation> operations = queryVO.getOperations();
//            QueryVO.QueryFieldVO fieldVO = new QueryVO.QueryFieldVO();
//            fieldVO.setField("entType");
//            fieldVO.setQueryType(EntSearchConditionEnum.MATCH_PHRASE.getCode());
//            fieldVO.setValue("个体工商户");
//            List<QueryVO.QueryFieldVO> list = new ArrayList<>();
//            list.add(fieldVO);
//            operations.add(new QueryVO.QueryOperation().setCondition(EntSearchOperationEnum.MUST_NOT.getType()).setQueryFields(list));
//            queryVO.setOperations(operations);
//        }
    }

    private void fixMoreThanPageSize(QueryVO queryVO) {
        Integer page = queryVO.getPageNo();
        Integer size = queryVO.getPageSize();
        if (page * size > 10000) {
            // 超过一万时， 从写pagesize
            queryVO.setPageSize(Math.floorDiv(10000, page));
        }
//        log.info("修正pageSize：{}", queryVO.toString());
    }

    private BoolQueryBuilder builderBoolQueryOperation(List<QueryVO.QueryOperation> queryOperations) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        for (QueryVO.QueryOperation operation : queryOperations) {
            if (EntSearchOperationEnum.isMust(operation.getCondition())) {
                buildMustQuery(boolQueryBuilder, operation);
            } else if (EntSearchOperationEnum.isShould(operation.getCondition())) {
                buildShouldQuery(boolQueryBuilder, operation);
            } else if (EntSearchOperationEnum.isMustNot(operation.getCondition())) {
                buildMustNotQuery(boolQueryBuilder, operation);
            }
        }
        return boolQueryBuilder;
    }

    @Override
    public QueryRespVO<EntCompany> queryLatest(QueryVO queryVO) {
        List<QueryVO.QueryOperation> defaultOperations = new ArrayList<>();
        QueryVO.QueryOperation vo = new QueryVO.QueryOperation();
        vo.setCondition(EntSearchOperationEnum.MUST.getType());
        List<QueryVO.QueryFieldVO> queryFieldVOList = new ArrayList<>();
        QueryVO.QueryFieldVO fieldVO = new QueryVO.QueryFieldVO();
        fieldVO.setField("startDate");

        String startDate = DateUtil.format(DateUtil.offsetDay(new Date(), -30), DatePattern.NORM_DATE_PATTERN);
        String endDate = DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATE_PATTERN);

        fieldVO.setValue(StrUtil.concat(true, startDate, EntConstants.RANGE_SPLIT_CODE, endDate));
        fieldVO.setQueryType(EntSearchConditionEnum.RANGE.getCode());
        queryFieldVOList.add(fieldVO);
        vo.setQueryFields(queryFieldVOList);
        defaultOperations.add(vo);
        queryVO.setOperations(defaultOperations);
        queryVO.setSortList(ListUtil.toList(new QueryVO.SortFieldVO().setField("startDate")));
        return queryList(queryVO);
    }


    @Override
    public List<NodeContactList> queryContactList(String cid) {
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        query.withQuery(QueryBuilders.termQuery(EntConstants.PRIMARY_KEY, cid));
        query.withPageable(PageRequest.ofSize(999));
        SearchHits<NodeContactList> search = elasticsearchRestTemplate.search(query.build(), NodeContactList.class);
        return search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public List<NodeContactList> queryContactListByIdList(List<String> idList) {
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        query.withQuery(QueryBuilders.termsQuery(EntConstants.PRIMARY_KEY, idList));
        // !Fixed bug: 领取时， 没有传分页参数，导致查询出来的号码列表只有10个
        query.withPageable(PageRequest.ofSize(999));
        SearchHits<NodeContactList> search = elasticsearchRestTemplate.search(query.build(), NodeContactList.class);
        return search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public Long countContact(String cid) {
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        query.withQuery(QueryBuilders.termQuery(EntConstants.PRIMARY_KEY, cid));
        return elasticsearchRestTemplate.count(query.build(), NodeContactList.class);
    }

    /**
     * 按 cid 查询 ent_company
     *
     * @author PetePower
     * @since 2024-01-25
     */
    @Override
    public EntCompany queryById(String cid) {
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        query.withQuery(QueryBuilders.termQuery(EntConstants.PRIMARY_KEY, cid));
        SearchHit<EntCompany> entCompanySearchHit = elasticsearchRestTemplate.searchOne(query.build(), EntCompany.class);
        assert entCompanySearchHit != null;
        Optional<SearchHit<EntCompany>> optional = Optional.of(entCompanySearchHit);
        return optional.get().getContent();
    }

    @Override
    public List<EntCompany> queryByIdList(List<String> idList) {
        if (CollUtil.isEmpty(idList)) {
            return Collections.emptyList();
        }
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        query.withQuery(QueryBuilders.termsQuery(EntConstants.PRIMARY_KEY, idList)).withPageable(PageRequest.ofSize(idList.size()));
        SearchHits<EntCompany> search = elasticsearchRestTemplate.search(query.build(), EntCompany.class);
        return search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public Map<String, String> getEntCompanyNameMap(List<String> idList) {
        List<EntCompany> list = queryByIdList(idList);
        Map<String, String> nameMap = new HashMap<>();
        list.forEach(data -> {
            nameMap.put(data.getCid(), data.getCompanyName());
        });
        return nameMap;
    }

    @Override
    public <T> QueryRespVO<T> queryNode(Class<T> clazz, String cid, Integer pageNo, Integer pageSize) {
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery(EntConstants.PRIMARY_KEY, cid));
        //shareholderInfoCount(股东信息),directors(主要人员) 需进行历史记录判断,历史记录不进行展示
//        List<String> needCheckHistory = Arrays.asList("NodeDirectors", "NodeShareholderInfo");
//        if (needCheckHistory.contains(clazz.getSimpleName())) {
//            //历史记录为1或不存在的
//            boolQueryBuilder.mustNot(QueryBuilders.termQuery("isHistoryData", 0));
////                    .should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("isHistoryData")));
//
//        }
        query.withQuery(boolQueryBuilder);
        // 这里有个傻逼的设定，pageIndex 默认是从0开始的， 但是为了和其他地方保持一致，参数里设定pageIndex从1 开始，到这里需要-1
        query.withPageable(PageRequest.of(pageNo - 1, pageSize));
        List<Sort.Order> orders = new ArrayList<>();
        if (StrUtil.equals(clazz.getSimpleName(), "NodeChangeRecord")) {
            orders.add(Sort.Order.desc("date"));
        } else if (StrUtil.equals(clazz.getSimpleName(), "NodeTenderBidding")) {
            orders.add(Sort.Order.desc("publishDate"));
        }
        query.withSort(Sort.by(orders));
        long totalHits = 0;
        List<T> resultList = new ArrayList<>();
        try {
            SearchHits<T> search = elasticsearchRestTemplate.search(query.build(), clazz);
            totalHits = search.getTotalHits();
            resultList = search.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("ES query error: {}", e.getMessage());
        }
        return new QueryRespVO<T>().setPageNo(pageNo).setPageSize(pageSize).setTotal(totalHits).setResults(resultList);
    }

    /**
     * 构建绝对&&查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private void buildMustQuery(BoolQueryBuilder boolQueryBuilder, QueryVO.QueryOperation operation) {
        List<QueryVO.QueryFieldVO> queryFields = operation.getQueryFields();
        // 这里修复一下， 如果queryFields 长度为1， 则使用must， 如果大于1 ，则使用should
        queryFields.forEach(v -> {
            String queryType = v.getQueryType();
            if (StrUtil.isNotEmpty(v.getCondition()) && StrUtil.equals(v.getCondition(), "not_in")) {
                queryType = "not_in";
            }
            if (StrUtil.isNotEmpty(v.getCondition()) && StrUtil.equals(v.getField(), "company_name") && StrUtil.equals(v.getCondition(), "match_phrase")) {
                queryType = "match_phrase";
                List<String> valueList = StrUtil.split(v.getValue(), ",");
                List<String> firstFiveElements = valueList.subList(0, Math.min(valueList.size(), 5));
                for (String value : firstFiveElements) {
                    boolQueryBuilder.must(buildSubQuery(v.getField(), value, queryType, v.getFormType()));
                }
            }else {
                boolQueryBuilder.must(buildSubQuery(v.getField(), v.getValue(), queryType, v.getFormType()));
            }

        });
        if (CollectionUtil.isNotEmpty(operation.getOperations())) {
            boolQueryBuilder.must(builderBoolQueryOperation(operation.getOperations()));
        }

    }

    /**
     * 构建 或 || 的查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private void buildShouldQuery(BoolQueryBuilder boolQueryBuilder, QueryVO.QueryOperation operation) {
        List<QueryVO.QueryFieldVO> queryFields = operation.getQueryFields();
        queryFields.forEach(v -> {
            String queryType = v.getQueryType();
            if (StrUtil.isNotEmpty(v.getCondition()) && StrUtil.equals(v.getCondition(), "not_in")) {
                queryType = "not_in";
            }

            if (StrUtil.isNotEmpty(v.getCondition()) && StrUtil.equals(v.getField(), "company_name") && StrUtil.equals(v.getCondition(), "match_phrase")) {
                queryType = "match_phrase";
                List<String> valueList = StrUtil.split(v.getValue(), ",");
                List<String> firstFiveElements = valueList.subList(0, Math.min(valueList.size(), 5));
                for (String value : firstFiveElements) {
                    boolQueryBuilder.should(buildSubQuery(v.getField(), value, queryType, v.getFormType()));
                }
            }else {
                boolQueryBuilder.should(buildSubQuery(v.getField(), v.getValue(), queryType, v.getFormType()));
            }
        });
        if (CollectionUtil.isNotEmpty(operation.getOperations())) {
            boolQueryBuilder.should(builderBoolQueryOperation(operation.getOperations()));
        }
    }

    /**
     * 构建全排除的 ！！ 的查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private void buildMustNotQuery(BoolQueryBuilder boolQueryBuilder, QueryVO.QueryOperation operation) {
        List<QueryVO.QueryFieldVO> queryFields = operation.getQueryFields();
        queryFields.forEach(v -> {
            String queryType = v.getQueryType();
            if (StrUtil.isNotEmpty(v.getCondition()) && StrUtil.equals(v.getCondition(), "not_in")) {
                queryType = "not_in";
            }
            if (StrUtil.isNotEmpty(v.getCondition()) && StrUtil.equals(v.getField(), "company_name") && StrUtil.equals(v.getCondition(), "match_phrase")) {
                queryType = "match_phrase";
                List<String> valueList = StrUtil.split(v.getValue(), ",");
                List<String> firstFiveElements = valueList.subList(0, Math.min(valueList.size(), 5));
                for (String value : firstFiveElements) {
                    boolQueryBuilder.mustNot(buildSubQuery(v.getField(), value, queryType, v.getFormType()));
                }
            }else {
                boolQueryBuilder.mustNot(buildSubQuery(v.getField(), v.getValue(), queryType, v.getFormType()));
            }

        });
        if (CollectionUtil.isNotEmpty(operation.getOperations())) {
            boolQueryBuilder.mustNot(builderBoolQueryOperation(operation.getOperations()));
        }
    }

    /**
     * 构建子查询树， 兼容普通查询和高级查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private QueryBuilder buildSubQuery(String input, String val, String condition, String formType) {
        EntSearchConditionEnum entSearchConditionEnum = EntSearchConditionEnum.getEnum(condition);
        // 做一下字段转换
        String field = convertField(input, val);
        String value = convertValue(input, val);

        if (field.equals("company_name")) {
            entReportProducer.sendEntReportMessage(EntSearchReportEnum.SEARCH.getType(), value);
        }
        //记录查询的关键字
        switch (entSearchConditionEnum) {
            // 弃用IN查询，全部使用 match 构建多层查询
            case IN:
                return buildMultiInQuery(field, value, formType);
            case NOT_IN:
                return buildMultiNotInQuery(field, value, formType);
            case TERM:
                return QueryBuilders.termQuery(field, value);
            case MATCH:
                if (field.equals("company_name")) {
                    return QueryBuilders.matchQuery(field, value).autoGenerateSynonymsPhraseQuery(false).boost(5.0f);
                }
                return QueryBuilders.matchQuery(field, value).autoGenerateSynonymsPhraseQuery(false);
            case MATCH_PHRASE:
                return QueryBuilders.matchPhraseQuery(field, value);
            case IS_OR_NOT:
                return buildIsOrNotQuery(field, value);
            case RANGE:
                return buildRangeQuery(field, value);
            case RANGE_DATE:
                return buildRangeDateQuery(field, value);
            case GEO_DISTANCE:
                return buildGeoDistanceQuery(field, value);
        }


        return null;
    }

    /**
     * 特殊字段的处理逻辑
     * 1. area : 所在地，前端用一个字段的值处理所选的所在地数据， 那么后端要根据具体的值，获取到省市县的对应字段
     *
     * @param input
     * @param value
     * @return
     * @author PetePower
     * @since 2024-02-22
     */
    private String convertField(String input, String value) {
        if (StrUtil.equals("area", input)) {
            return EntAreaConvert.getFieldByCode(value);
        }
        return input;
    }

    private String convertValue(String field, String value) {
        if (StrUtil.equals("startDate", field)) {
            String startDate = "";
            String endDate = "";
            if (StrUtil.equals("1", value)) {
                // 小于1年
                startDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12), DatePattern.NORM_DATE_PATTERN);
                endDate = DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATE_PATTERN);
            } else if (StrUtil.equals("1-5", value)) {
                // 1-5 年
                startDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12 * 5), DatePattern.NORM_DATE_PATTERN);
                endDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12), DatePattern.NORM_DATE_PATTERN);
            } else if (StrUtil.equals("5-10", value)) {
                // 5-10 年
                startDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12 * 10), DatePattern.NORM_DATE_PATTERN);
                endDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12 * 5), DatePattern.NORM_DATE_PATTERN);
            } else if (StrUtil.equals("10", value)) {
                // 10 年 以上 -- 开始时间为100年前，应该不会有杠精吧~
                startDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12 * 100), DatePattern.NORM_DATE_PATTERN);
                endDate = DateUtil.format(DateUtil.offsetMonth(new Date(), -12 * 10), DatePattern.NORM_DATE_PATTERN);
            }
            if (StrUtil.isEmpty(startDate) || StrUtil.isEmpty(endDate)) {
                return value;
            }
            return StrUtil.concat(true, startDate, EntConstants.RANGE_SPLIT_CODE, endDate);
        }
        // 企业类型转换
        else if (StrUtil.equals(field, "entType")) {
            return CollUtil.join(EsQueryValueConvertUtils.convertValues(value), ",");
        }
        // 融资阶段转换
        else if (StrUtil.equals(field, "finance_now_info")) {
            return CollUtil.join(EsQueryValueConvertUtils.convertValues(value), ",");
        }

        return value;
    }

//    private List<String> convertValue(String field, List<String> value) {
//        if (StrUtil.equals(field, "entType")) {
//            value = EsQueryValueConvertUtils.getIndustryValues(value);
//        }
//        return value;
//    }


    /**
     * 构建多层 IN 查询 -> 完了采用 MATCH 的查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private QueryBuilder buildMultiInQuery(String field, String value, String formType) {

        if (StrUtil.isEmpty(value)) {
            throw new ServiceException(QUERY_ERROR_MISS_PARAMS);
        }
        List<String> value1 = StrUtil.split(value, EntConstants.RANGE_SPLIT_CODE);
        if (value1.isEmpty()) {
            throw new ServiceException(QUERY_ERROR_MISS_PARAMS);
        }
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (formType != null && formType.equals(EntFormTypeEnum.CHECKBOX.getType())) {
            //对需要特殊处理的字段值进行转换
            if (EntSearchFieldNotKeywordEnum.contains(field)) {
                boolQueryBuilder.must(QueryBuilders.termsQuery(field, value1.toArray(new String[0])));
            } else {
                boolQueryBuilder.must(QueryBuilders.termsQuery(field + ".keyword", value1.toArray(new String[0])));
            }
        } else {
            value = value.replace(",", " ");
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery(value, field));
        }
        return boolQueryBuilder;
    }

    private QueryBuilder buildMultiNotInQuery(String field, String value, String formType) {

        if (StrUtil.isEmpty(value)) {
            throw new ServiceException(QUERY_ERROR_MISS_PARAMS);
        }
        List<String> value1 = StrUtil.split(value, EntConstants.RANGE_SPLIT_CODE);
        if (value1.isEmpty()) {
            throw new ServiceException(QUERY_ERROR_MISS_PARAMS);
        }
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (formType != null && formType.equals(EntFormTypeEnum.CHECKBOX.getType())) {
            //对需要特殊处理的字段值进行转换
            if (EntSearchFieldNotKeywordEnum.contains(field)) {
                boolQueryBuilder.mustNot(QueryBuilders.termsQuery(field, value1.toArray(new String[0])));
            } else {

                boolQueryBuilder.mustNot(QueryBuilders.termsQuery(field + ".keyword", value1.toArray(new String[0])));
            }
        } else {
            boolQueryBuilder.mustNot(QueryBuilders.multiMatchQuery(value, field));
        }
        return boolQueryBuilder;
    }

    /**
     * 构建 有/无 是/否的查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private QueryBuilder buildIsOrNotQuery(String field, String value) {
        if (ObjUtil.equals(0, Integer.parseInt(value))) {
            return QueryBuilders.termQuery(field, 0);
        }
        // 有 -> 查询不等于，速度比区间要快很多
        // 修改为一个子查询，must_not
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//        boolQueryBuilder.filter(QueryBuilders.existsQuery(field));

        boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, 0));
        // TODO: 等字段补好后， 再恢复这个代码
//        QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(field, 0));
        return boolQueryBuilder;

//        return QueryBuilders.rangeQuery(field).gte(1);
    }

    /**
     * 构建区间查询
     *
     * @author PetePower
     * @since 2024-01-25
     */
    private QueryBuilder buildRangeQuery(String field, String value) {
        List<String> split = StrUtil.split(value, EntConstants.RANGE_SPLIT_CODE);
        if (split.size() != 2) {
            throw new ServiceException(new ErrorCode(100500, "区间范围长度有误，必须为2位"));
        }
        return QueryBuilders.rangeQuery(field).gte(split.get(0)).lt(split.get(1));
    }

    /**
     * 构建时间区间查询
     *
     * @param field
     * @param value
     * @return
     */
    private QueryBuilder buildRangeDateQuery(String field, String value) {
        List<String> split = StrUtil.split(value, EntConstants.RANGE_SPLIT_CODE);
        if (split.size() != 2) {
            throw new ServiceException(new ErrorCode(100500, "时间区间范围有误，必须为2位"));
        }
        return QueryBuilders.rangeQuery(field).gte(split.get(0)).lt(split.get(1));
    }

    /**
     * 构建距离查询
     *
     * @author lzc
     * @since 2024-07-17
     */
    private QueryBuilder buildGeoDistanceQuery(String field, String value) {
        List<String> split = StrUtil.split(value, EntConstants.RANGE_SPLIT_CODE);

        if (split.size() != 3) {
            throw new ServiceException(new ErrorCode(100500, "经纬度参数异常"));
        }
        double dis = Double.parseDouble(split.get(0)); //距离(千米)
        double lng = Double.parseDouble(split.get(1)); //经度
        double lat = Double.parseDouble(split.get(2)); //纬度
        // es查询附近 数据修改后使用
        return QueryBuilders.geoDistanceQuery(field).distance(dis, DistanceUnit.KILOMETERS)
                .point(lat, lng);

//        double earthRadius = 6371;//地球半径(千米)
//        // 计算经度弧度,从弧度转换为角度
//        double dLongitude = 2 * (Math.asin(Math.sin(dis / (2 * earthRadius)) / Math.cos(Math.toRadians(lat))));
//        dLongitude = Math.toDegrees(dLongitude);
//        // 计算纬度角度
//        double dLatitude = dis / earthRadius;
//        dLatitude = Math.toDegrees(dLatitude);
//        // 正方形的左上角和右下角
//        double minlat = lat - dLatitude;
//        double maxlat = lat + dLatitude;
//        double minlng = lng - dLongitude;
//        double maxlng = lng + dLongitude;
//        return QueryBuilders.boolQuery()
//                .must(QueryBuilders.rangeQuery(field + ".lat").gte(minlat).lte(maxlat))
//                .must(QueryBuilders.rangeQuery(field + ".lng").gte(minlng).lte(maxlng));

    }
}
